package com.orkasgb.framework.echarts;

import com.alibaba.fastjson.JSONObject;
import com.github.abel533.echarts.Option;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.web.client.RestTemplate;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Base64;

public abstract class AbstractChartEchartsConvert implements EChartsConvert {

    private static final Logger LOG = LoggerFactory.getLogger(AbstractChartEchartsConvert.class);

    private static final String URL_SUFFIX = "/echarts-convert?opt={options}";

    private static final String SERVER_URL = "http://10.254.5.93:39999";

    private static final String FILE_SUFFIX = ".png";

    private static final String BASE64_PREFIX = "data:image/png;base64,";

    private static final String DEFAULT_LINUX_FILE_PATH = "/app/tmp/echarts/";

    private static final String DEFAULT_WIN_FILE_PATH = "F:\\tmp\\echarts\\";

    private static final String ECHARTS_CONVERT_JS_WIN_PATH = "E:\\echartsconvert\\echarts-convert.js";

    private static final String ECHARTS_CONVERT_JS_LINUX_PATH = "/aikosolar/software/echartsconvert/echarts-convert.js";

    public String convertFromOption() {
        Option option = this.convertChartOption();
        Assert.notNull(option, "option is null");

        return this.convertChartFile(option);
    }

    public String convertFromJsonString() {
        String optionString = this.convertChartJsonString();
        Assert.notNull(optionString, "optionString is null");

        return this.convertChartFile(optionString);
    }

    public String convertFromScript() {
        LOG.info("convert from script");
        String filePath = "";
        String os = System.getProperty("os.name").toLowerCase();
        if (os.contains("linux")) {
            filePath = this.convertFromShell();
        } else if (os.contains("win")) {
            filePath = this.convertFromCmd();
        }

        return filePath;
    }

    private String convertFromShell() {
        String optionString = this.convertChartJsonString();
        Assert.hasText(optionString, "optionString is empty or null");
        ProcessBuilder processBuilder = new ProcessBuilder();
        String filePathStr = DEFAULT_LINUX_FILE_PATH + System.currentTimeMillis() + "_chart_" + FILE_SUFFIX;
        String command = "phantomjs " + ECHARTS_CONVERT_JS_LINUX_PATH + " -o " + optionString + " -t file -f " + filePathStr;
        processBuilder.command("shell", "-c", command);
        try {
            Process process = processBuilder.start();
            BufferedReader reader = new BufferedReader(
                    new InputStreamReader(process.getInputStream()));

            String line;
            while ((line = reader.readLine()) != null) {
                LOG.info("result of execute command: {}\n", line);
            }

            int exitVal = process.waitFor();
            if (exitVal != 0) {
                LOG.error("Failed to execute command, exit code: {}", exitVal);
            }

        } catch (IOException | InterruptedException e) {
            LOG.error("Failed to execute command", e);
        }

        return filePathStr;
    }

    public String convertFromCmd() {
        String optionString = this.convertChartJsonString();
        Assert.hasText(optionString, "optionString is empty or null");
        ProcessBuilder processBuilder = new ProcessBuilder();
        String optionStr = StringUtils.replaceEach(optionString, new String[]{"\"", " ", "%", "\n"}, new String[]{"'", "", "%25", ""});
        String filePathStr = DEFAULT_WIN_FILE_PATH + System.currentTimeMillis() + "_chart_" + FILE_SUFFIX;
        String command = "phantomjs " + ECHARTS_CONVERT_JS_WIN_PATH + " -o " + optionStr + " -t file -f " + filePathStr;
        processBuilder.command("cmd.exe", "/c", command);
        try {
            Process process = processBuilder.start();
            BufferedReader reader = new BufferedReader(
                    new InputStreamReader(process.getInputStream()));

            String line;
            while ((line = reader.readLine()) != null) {
                LOG.info("result of execute command: {}\n", line);
            }

            int exitVal = process.waitFor();
            if (exitVal != 0) {
                LOG.error("Failed to execute command, exit code: {}", exitVal);
            }

        } catch (IOException | InterruptedException e) {
            LOG.error("Failed to execute command", e);
        }

        return filePathStr;
    }

    public abstract Option convertChartOption();

    public abstract String convertChartJsonString();

    private String convertChartFile(Option option) {
        String result = this.generateChartFromServer(option);
        return this.convertFile(result);
    }

    private String convertChartFile(String optionString) {
        String result = this.generateChartFromServer(optionString);
        return this.convertFile(result);
    }

    private String generateChartFromServer(Option option) {
        String result = "";
        try {
            String optionString = StringUtils.replaceEach(JSONObject.toJSONString(option), new String[]{"\"", " ", "%", "\n"}, new String[]{"'", "", "%25", ""});
            RestTemplate restTemplate = new RestTemplate();
            String url = SERVER_URL + URL_SUFFIX;
            JSONObject body = restTemplate
                    .getForEntity(url, JSONObject.class, optionString).getBody();
            Assert.notNull(body, "body is null");
            result = body.getString("data");
        } catch (Exception e) {
            LOG.error("Failed to generate chart from server", e);
        }

        return result;
    }

    private String generateChartFromServer(String optionString) {
        String result = "";
        try {
            String optionStr = StringUtils.replaceEach(optionString, new String[]{"\"", " ", "%", "\n"}, new String[]{"'", "", "%25", ""});
            RestTemplate restTemplate = new RestTemplate();
            String url = SERVER_URL + URL_SUFFIX;
            JSONObject body = restTemplate
                    .getForEntity(url, JSONObject.class, optionStr).getBody();
            Assert.notNull(body, "body is null");
            result = body.getString("data");
        } catch (Exception e) {
            LOG.error("Failed to generate chart from server", e);
        }

        return result;
    }

    private String convertFile(String base64String) {
        String imagePath = ""; // 图片将保存到这个路径
        // 解码Base64字符串
        byte[] imageBytes = Base64.getDecoder().decode(base64String);
        try {
            // 创建图片
            BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageBytes));
            // 保存图片到文件
            String os = System.getProperty("os.name").toLowerCase();
            if (os.contains("linux")) {
                imagePath = DEFAULT_LINUX_FILE_PATH + System.currentTimeMillis() + "_chart_" + FILE_SUFFIX;
            } else if (os.contains("win")) {
                imagePath = DEFAULT_WIN_FILE_PATH + System.currentTimeMillis() + "_chart_" + FILE_SUFFIX;
            }
            File outputFile = new File(imagePath);
            if (!outputFile.exists()) {
                boolean mkdirs = outputFile.getParentFile().mkdirs();
                if (!mkdirs) {
                    LOG.error("Failed to create directory: {}", outputFile.getParentFile());
                }
            }
            ImageIO.write(image, "png", outputFile);
            imagePath = outputFile.getAbsolutePath();
        } catch (IOException e) {
            LOG.error("Failed to convert base64 string to image", e);
        }

        return imagePath;
    }
}
